from dataclasses import dataclass
import enum
from functools import cached_property
from tqdm import tqdm
import pandas as pd

import os
import sys

project_dir_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
sys.path.append(project_dir_path)
print(project_dir_path)


class UKBioBankValueType(enum.Enum):
    """
    The value type of data-field, describing the type of variable corresponding to it.

    There are following categories:
        11-Integer - whole numbers, for example the age of a participant on a particular date;
        21-Categorical (single) - a single answer selected from a coded list or tree of mutually exclusive options, for example a yes/no choice;
        22-Categorical (multiple) - sets of answers selected from a coded list or tree of options, for instance concurrent medications;
        31-Continuous - floating-point numbers, for example the height of a participant;
        41-Text - data composed of alphanumeric characters, for example the first line of an address;
        51-Date - a calendar date, for example 14th October 2010;
        61-Time - a time, for example 13:38:05 on 14th October 2010;
        101-Compound - a set of values required as a whole to describe some compound property, for example an ECG trace;
    """
    Integer = 11
    CategoricalSingle = 21
    CategoricalMultiple = 22
    Continuous = 31
    Text = 41
    Date = 51
    Time = 61
    Compound = 101


@dataclass(kw_only=True)
class UKBioBankInstance:
    instance_id: int
    index_and_descript: dict[int, str]  # index和descript为键值对



@dataclass(kw_only=True)
class UKBioBankField:
    field_id: str
    value_type: UKBioBankValueType
    encoding_id: int
    instance_id: int
    category_id: int = None
    title: str = None
    notes: str = None


@dataclass(kw_only=True)
class UKBioBankEncoding:
    new_value: int
    # 此处为new value对应的意思
    meaning: str



@dataclass(kw_only=True)
class UKBioBankMainCategory:  # 记录列的父类别
    category_id: int
    title: str
    descript: str = None
    notes: str = None



class UKBioBankProcessor:

    def __init__(self, id_file_path: str = None, encoding_file_path: str = None, instance_file_path: str = None, categories_file_path: str = None):
        self.id_file_path = id_file_path
        self.encoding_file_path = encoding_file_path
        self.instance_file_path = instance_file_path
        self.categories_file_path = categories_file_path

    @cached_property
    def fields(self) -> dict[int, UKBioBankField]:
        fields_df = pd.read_csv(self.id_file_path)
        # field_id列为fields的key和UKBioBankCategoryField的field_id, value_type列为UKBioBankCategoryField中UKBioBankValueType的枚举值, encoding_id列UKBioBankCategoryField为的encoding_id
        fields_map = {}
        for _, row in tqdm(fields_df.iterrows(), total=len(fields_df), desc="Processing fields"):
            field_id = str(row['field_id'])
            value_type = UKBioBankValueType(row['value_type'])
            encoding_id = int(row['encoding_id'])
            instance_id = int(row['instance_id'])
            category_id = int(row['main_category'])
            title = row['title']
            notes = row.get('notes', None)

            fields_map[field_id] = UKBioBankField(
                field_id=field_id,
                value_type=value_type,
                encoding_id=encoding_id,
                instance_id=instance_id,
                category_id=category_id,
                title=title,
                notes=notes
            )
        return fields_map

    @cached_property
    def encodings(self) -> dict[int, UKBioBankEncoding]:
        encodings_df = pd.read_csv(self.encoding_file_path)
        # encodings_df中encoding_id为new_value meaning为meaning
        encodings_map = {}
        for _, row in tqdm(encodings_df.iterrows(), total=len(encodings_df), desc="Processing encodings"):
            new_value = int(row['new_value'])
            meaning = row['meaning']

            encodings_map[new_value] = UKBioBankEncoding(
                new_value=new_value,
                meaning=meaning
            )
        return encodings_map

    @cached_property
    def instances(self) -> dict[int, UKBioBankInstance]:

        instances_df = pd.read_csv(self.instance_file_path)
        # instance_id列为instance的key, index列为index, descript列为descript
        instances_map = {}
        for _, row in tqdm(instances_df.iterrows(), total=len(instances_df), desc="Processing instances"):
                instance_id = int(row['instance_id'])
                index = int(row['index'])
                descript = row['descript']

                if instance_id not in instances_map:
                    instances_map[instance_id] = UKBioBankInstance(
                        instance_id=instance_id,
                        index_and_descript={}
                    )
                # 将index和descript作为键值对存入UKBioBankInstance的index_and_descript字典中
                instances_map[instance_id].index_and_descript[index] = descript
        return instances_map

    @cached_property
    def categories(self) -> dict[int, UKBioBankMainCategory]:
        categories_df = pd.read_csv(self.categories_file_path)
        # category_id列为category的key, title列为title, descript列为descript, notes列为notes
        categories_map = {}
        for _, row in tqdm(categories_df.iterrows(), total=len(categories_df), desc="Processing categories"):
            category_id = int(row['category_id'])
            title = row['title']
            descript = row.get('descript', None)
            notes = row.get('notes', None)

            categories_map[category_id] = UKBioBankMainCategory(
                category_id=category_id,
                title=title,
                descript=descript,
                notes=notes
            )
        return categories_map


if __name__ == '__main__':
    id_file_path = os.path.join(project_dir_path, "datasets/raw/ukbiobank/v3/id.csv")
    encoding_file_path = os.path.join(project_dir_path, "datasets/raw/ukbiobank/v3/encoding.csv")
    instance_file_path = os.path.join(project_dir_path, "datasets/raw/ukbiobank/v3/instance.csv")
    categories_file_path = os.path.join(project_dir_path, "datasets/raw/ukbiobank/v3/category.csv")

    processor = UKBioBankProcessor(id_file_path=id_file_path, encoding_file_path=encoding_file_path,
                                   instance_file_path=instance_file_path,
                                   categories_file_path=categories_file_path)
    fields = processor.fields
    print(f"Processed {len(fields)} fields.")
    # print(fields)

    encodings = processor.encodings
    print(f"Processed {len(encodings)} encodings.")
    # print(encodings)

    instances = processor.instances
    print(f"Processed {len(instances)} instances.")
    # print(instances)

    categories = processor.categories
    print(f"Processed {len(categories)} categories.")
    # print(categories)
